home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / Snippets / Sound / Sound Input / SoundInputSample.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-03-14  |  12.3 KB  |  394 lines  |  [TEXT/CWIE]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    SoundInputSample: Sample code to properly record a sound to disk and play it back.
  5. **
  6. **    by Andrew Wulf, Apple Developer Technical Support
  7. **
  8. **    File:        SoundInputSample.c
  9. **
  10. **    Copyright © 1996 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Quickdraw.h>
  23. #include <Windows.h>
  24. #include <Dialogs.h>
  25. #include <Files.h>
  26. #include <StandardFile.h>
  27. #include <Packages.h>
  28. #include <OSEvents.h>
  29. #include <Memory.h>
  30. #include <Sound.h>    
  31. #include <SoundInput.h>
  32. #include <SoundComponents.h>
  33. #include <OSUtils.h>
  34. #include <ToolUtils.h>
  35. #include <Types.h>
  36. #include <TextUtils.h>
  37. #include <SegLoad.h>
  38. #include <Gestalt.h>
  39. #include <AIFF.h>
  40. #include <stdio.h>
  41. #include <strings.h>
  42.  
  43. #if PRAGMA_ALIGN_SUPPORTED
  44. #pragma options align=mac68k
  45. #endif
  46.  
  47. typedef struct
  48.     {
  49.     short numberOfRatesSupported;
  50.     unsigned long** ratesSupported;
  51.     } SampleRatesData;
  52.  
  53. typedef struct
  54.     {
  55.     short numberOfSizesSupported;
  56.     short** sizesSupported;
  57.     } SampleSizesData;
  58.  
  59. #if PRAGMA_ALIGN_SUPPORTED
  60. #pragma options align=reset
  61. #endif
  62.  
  63. #if ((!defined(__MWERKS__))&&(!defined(THINK_C)))
  64.     QDGlobals qd;
  65. #endif
  66.  
  67. void ShowErrAndQuit(char*text, short err);
  68. void ShowErr(char*text, short err);
  69.  
  70. void main()
  71.     {
  72.     short                 i;
  73.     OSErr                 err;
  74.  
  75.     long                 soundAttr                 = 0;
  76.     SndChannelPtr         mySoundChannel;
  77.     StandardFileReply     fileReply;
  78.     short                 myFRefNum;
  79.     long                 mySIRefNum;
  80.  
  81.     SampleSizesData     sampleSizesInfo         = { 0, 0 };
  82.     SampleRatesData     sampleRatesInfo         = { 0, 0 };
  83.  
  84.     OSType                 previousCompressionType;
  85.     short                 previousTwosComplement;
  86.     short                 previousChannels;
  87.     unsigned long        previousSampleRate;
  88.     short                 previousSampleSize;
  89.     Fixed                 previousGain;
  90.     short                 previousVoxRecord[2];
  91.     short                 previousVoxStop[3];
  92.  
  93.     OSType                 noCompression             = NoneType;
  94.  
  95.     OSType                 compressionSetting         = noCompression;
  96.     short                 twosComplementSetting     = 1;
  97.     short                 numChannels             = 1;
  98.     unsigned long         sampleRate;
  99.     short                 sampleSize;
  100.     long                 soundDuration             = 4*1000;     // in milliseconds
  101.     Fixed                 inputGain                 = 0x00010000L;    // = 1.0, can be up to 1.5
  102.     short                 voxRecord[2]             = { 0, 0 };
  103.     short                 voxStop[3]                 = { 0, 0, 0 };
  104.     
  105.     SPB                 soundParamBlock;
  106.     ParamBlockRec         paramBlock;
  107.     
  108.     DialogPtr             infoDialog                = 0;
  109.  
  110.     InitGraf(&qd.thePort);
  111.     FlushEvents(everyEvent, 0);
  112.     InitWindows();
  113.     InitDialogs(0);
  114.     InitCursor();
  115.     
  116.     // check gestalt to see if we have sound input support
  117.     
  118.     err = Gestalt(gestaltSoundAttr, &soundAttr);
  119.     
  120.     if (err || (soundAttr & (1L<<gestaltSoundIOMgrPresent))==0
  121.             || (soundAttr & (1L<<gestaltBuiltInSoundInput))==0)
  122.         ShowErrAndQuit("Insufficient sound input support", 0);
  123.     
  124.     // Make a file to save sound into
  125.     
  126.     StandardPutFile("\pPick a file to record to:", "\p", &fileReply);
  127.     if (! fileReply.sfGood)
  128.         ShowErrAndQuit("Goodbye", 0);
  129.  
  130.     FSpCreate(&fileReply.sfFile, 'SCPL', 'AIFC', 0);    // a SoundApp creator & filetype
  131.     err = FSpOpenDF(&fileReply.sfFile, fsRdWrPerm, &myFRefNum);
  132.     if (err != noErr)
  133.         ShowErrAndQuit("FSpOpenDF", err);
  134.                 
  135.     // Open the default sound input device
  136.     
  137.     err = SPBOpenDevice("\p", siWritePermission, &mySIRefNum);
  138.     if (err != noErr)
  139.         ShowErrAndQuit("SPBOpenDevice", err);
  140.         
  141.     // Get the sample sizes available
  142.     
  143.     err = SPBGetDeviceInfo(mySIRefNum, siSampleSizeAvailable, (Ptr) &sampleSizesInfo);
  144.     if (err != noErr)
  145.         ShowErrAndQuit("SPBGetDeviceInfo with siSampleSizeAvailable", err);
  146.     
  147.     // Get the sample rates supported
  148.  
  149.     err = SPBGetDeviceInfo(mySIRefNum, siSampleRateAvailable, (Ptr) &sampleRatesInfo);
  150.     if (err != noErr)
  151.         ShowErrAndQuit("SPBGetDeviceInfo with siSampleRateAvailable", err);
  152.     
  153.     // get the current settings of the sound input device; to be friendly, we'll set them
  154.     // back after we're through
  155.     
  156.     err = SPBGetDeviceInfo(mySIRefNum, siInputGain, (Ptr) &previousGain);
  157.     // ignore error, input device isn't required to support this
  158.  
  159.     err = SPBGetDeviceInfo(mySIRefNum, siVoxRecordInfo, (Ptr) previousVoxRecord);
  160.     // ignore error, input device isn't required to support this
  161.  
  162.     err = SPBGetDeviceInfo(mySIRefNum, siVoxStopInfo, (Ptr) previousVoxStop);
  163.     // ignore error, input device isn't required to support this
  164.  
  165.     err = SPBGetDeviceInfo(mySIRefNum, siCompressionType, (Ptr) &previousCompressionType);
  166.     if (err != noErr)
  167.         ShowErrAndQuit("SPBGetDeviceInfo with siCompressionType", err);
  168.  
  169.     err = SPBGetDeviceInfo(mySIRefNum, siNumberChannels, (Ptr) &previousChannels);
  170.     if (err != noErr)
  171.         ShowErrAndQuit("SPBGetDeviceInfo with siNumberChannels", err);
  172.  
  173.     err = SPBGetDeviceInfo(mySIRefNum, siSampleRate, (Ptr) &previousSampleRate);
  174.     if (err != noErr)
  175.         ShowErrAndQuit("SPBGetDeviceInfo with siSampleRate", err);
  176.  
  177.     err = SPBGetDeviceInfo(mySIRefNum, siSampleSize, (Ptr) &previousSampleSize);
  178.     if (err != noErr)
  179.         ShowErrAndQuit("SPBGetDeviceInfo with siSampleSize", err);
  180.  
  181.     err = SPBGetDeviceInfo(mySIRefNum, siTwosComplementOnOff, (Ptr) &previousTwosComplement);
  182.     if (err != noErr)
  183.         ShowErrAndQuit("SPBGetDeviceInfo with siTwosComplementOnOff", err);
  184.     
  185.     // pick the biggest sample size available
  186.     
  187.     sampleSize = 0;
  188.     for (i=0; i< sampleSizesInfo.numberOfSizesSupported; i++)
  189.         if ((*sampleSizesInfo.sizesSupported)[i] > sampleSize)
  190.             sampleSize = (*sampleSizesInfo.sizesSupported)[i];
  191.     
  192.     // MACE compression only supported in 8 bit
  193.     
  194.     if ( compressionSetting == kMace3SubType || compressionSetting == kMace6SubType )
  195.         {
  196.         sampleSize = 8;
  197.         twosComplementSetting = 0;
  198.         }
  199.     
  200.     // now pick the best sample rate available, if the count is 0, you get a range of [low, high]
  201.     // note the sample rate is actually treated as an "unsigned" fixed value
  202.     
  203.     if (sampleRatesInfo.numberOfRatesSupported == 0)
  204.         sampleRate = (*sampleRatesInfo.ratesSupported)[ 1 ];
  205.     else
  206.         {
  207.         sampleRate = 0;
  208.         for (i=0; i< sampleRatesInfo.numberOfRatesSupported; i++)
  209.             if ((*sampleRatesInfo.ratesSupported)[i] > sampleRate)
  210.                 sampleRate = (*sampleRatesInfo.ratesSupported)[i];
  211.         }
  212.     
  213.     //-------------------------------------------------------------------------------
  214.     // now we'll set the parameters we want - this is very important to do, since you
  215.     // won't get consistant results otherwise. Never assume you know the state! You
  216.     // share the input driver's state with every other application.
  217.     //-------------------------------------------------------------------------------
  218.     
  219.     // initialize the compression type to NONE, if you want compression, set it last
  220.     // note that not all devices can compress sound during input, and many
  221.     // compressions are not available.
  222.  
  223.     err = SPBSetDeviceInfo(mySIRefNum, siCompressionType, (Ptr) &noCompression);
  224.     if (err != noErr)
  225.         ShowErrAndQuit("SPBSetDeviceInfo with siCompressionType == NONE", err);
  226.  
  227.     err = SPBSetDeviceInfo(mySIRefNum, siNumberChannels, (Ptr) &numChannels);
  228.     if (err != noErr)
  229.         ShowErrAndQuit("SPBSetDeviceInfo with siNumberChannels", err);
  230.  
  231.     err = SPBSetDeviceInfo(mySIRefNum, siSampleSize, (Ptr) &sampleSize);
  232.     if (err != noErr)
  233.         ShowErrAndQuit("SPBSetDeviceInfo with siSampleSize", err);
  234.  
  235.     err = SPBSetDeviceInfo(mySIRefNum, siSampleRate, (Ptr) &sampleRate);
  236.     if (err != noErr)
  237.         ShowErrAndQuit("SPBSetDeviceInfo with siSampleRate", err);
  238.  
  239.     err = SPBSetDeviceInfo(mySIRefNum, siTwosComplementOnOff, (Ptr) &twosComplementSetting);
  240.     if (err != noErr)
  241.         ShowErrAndQuit("SPBSetDeviceInfo with siTwosComplementOnOff", err);
  242.     
  243.     // you may get err == siInvalidCompression(-223) if it's not supported for input
  244.     
  245.     if ( compressionSetting != noCompression )
  246.         {
  247.         err = SPBSetDeviceInfo(mySIRefNum, siCompressionType, (Ptr) &compressionSetting);
  248.         if (err != noErr)
  249.             ShowErrAndQuit("SPBSetDeviceInfo with siCompressionType", err);
  250.         }
  251.  
  252.     err = SPBSetDeviceInfo(mySIRefNum, siInputGain, (Ptr) &inputGain);
  253.     // ignore error, input device isn't required to support this
  254.     
  255.     err = SPBSetDeviceInfo(mySIRefNum, siVoxRecordInfo, (Ptr) voxRecord);
  256.     // ignore error, input device isn't required to support this
  257.  
  258.     err = SPBSetDeviceInfo(mySIRefNum, siVoxStopInfo, (Ptr) voxStop);
  259.     // ignore error, input device isn't required to support this
  260.  
  261.     // note that you could use siRecordingQuality to set the rate, sample size & compression
  262.     // 'best', 'better' and 'good' quality are defined by the sound input device,
  263.     // so use this if you really don't care what you get
  264.     
  265.     // now setup the AIFF file header
  266.     
  267.     err = SetupAIFFHeader(myFRefNum, numChannels, sampleRate, sampleSize,
  268.                                                 compressionSetting, 0, 0);
  269.     if (err != noErr)
  270.         ShowErrAndQuit("SetupAIFFHeader", err);
  271.     
  272.     // actually record the sound to file
  273.     
  274.     infoDialog = GetNewDialog(129, 0L, (WindowPtr)-1 );
  275.     ShowWindow( infoDialog );
  276.     DrawDialog( infoDialog );
  277.     
  278.     soundParamBlock.inRefNum = mySIRefNum;
  279.     soundParamBlock.count = 0;                // after recording, this holds the # bytes recorded
  280.     soundParamBlock.milliseconds = soundDuration;
  281.     soundParamBlock.completionRoutine = 0;
  282.     soundParamBlock.interruptRoutine = 0;
  283.     soundParamBlock.userLong = 0;
  284.     soundParamBlock.error = 0;
  285.     soundParamBlock.unused1 = 0;
  286.     
  287.     err = SPBRecordToFile(myFRefNum, &soundParamBlock, false);    // synchronous
  288.     if (err != noErr)
  289.         ShowErrAndQuit("Failure at call to SPBRecordToFile", err);
  290.     
  291.     CloseDialog( infoDialog );
  292.     
  293.     // set the input parameters back the way they were
  294.     
  295.     err = SPBSetDeviceInfo(mySIRefNum, siCompressionType, (Ptr) &previousCompressionType);
  296.     if (err != noErr)
  297.         ShowErrAndQuit("SPBSetDeviceInfo resetting siCompressionType", err);
  298.  
  299.     err = SPBSetDeviceInfo(mySIRefNum, siNumberChannels, (Ptr) &previousChannels);
  300.     if (err != noErr)
  301.         ShowErrAndQuit("SPBSetDeviceInfo resetting siNumberChannels", err);
  302.  
  303.     err = SPBSetDeviceInfo(mySIRefNum, siSampleRate, (Ptr) &previousSampleRate);
  304.     if (err != noErr)
  305.         ShowErrAndQuit("SPBSetDeviceInfo resetting siSampleRate", err);
  306.  
  307.     err = SPBSetDeviceInfo(mySIRefNum, siSampleSize, (Ptr) &previousSampleSize);
  308.     if (err != noErr)
  309.         ShowErrAndQuit("SPBSetDeviceInfo resetting siSampleSize", err);
  310.  
  311.     err = SPBSetDeviceInfo(mySIRefNum, siTwosComplementOnOff, (Ptr) &previousTwosComplement);
  312.     if (err != noErr)
  313.         ShowErrAndQuit("SPBSetDeviceInfo resetting siTwosComplementOnOff", err);
  314.  
  315.     err = SPBSetDeviceInfo(mySIRefNum, siInputGain, (Ptr) &previousGain);
  316.     // ignore error, input device isn't required to support this
  317.  
  318.     err = SPBSetDeviceInfo(mySIRefNum, siVoxRecordInfo, (Ptr) previousVoxRecord);
  319.     // ignore error, input device isn't required to support this
  320.  
  321.     err = SPBSetDeviceInfo(mySIRefNum, siVoxStopInfo, (Ptr) previousVoxStop);
  322.     // ignore error, input device isn't required to support this
  323.  
  324.     // We're done recording so close the device
  325.  
  326.     err = SPBCloseDevice(mySIRefNum);
  327.     if (err != noErr)
  328.         ShowErrAndQuit("SPBCloseDevice", err);
  329.     
  330.      // rewind sound file to start 
  331.  
  332.      paramBlock.ioParam.ioCompletion = 0;
  333.     paramBlock.ioParam.ioRefNum = myFRefNum;
  334.     paramBlock.ioParam.ioPosMode = fsFromStart;
  335.     paramBlock.ioParam.ioPosOffset = 0;
  336.  
  337.     err = PBSetFPos(¶mBlock, false);
  338.     if (err != noErr)
  339.         ShowErrAndQuit("PBSetFPos", err);
  340.     
  341.     // now resetup file header with actual count of bytes
  342.  
  343.     err = SetupAIFFHeader(myFRefNum, numChannels, sampleRate, sampleSize,
  344.                             compressionSetting, soundParamBlock.count, 0);
  345.     if (err != noErr)
  346.         ShowErrAndQuit("SetupAIFFHeader 2nd time", err);
  347.     
  348.     // Allocate our own sound channel
  349.     
  350.     mySoundChannel = 0;
  351.     err = SndNewChannel(&mySoundChannel, sampledSynth, 0, 0);
  352.     if (err != noErr)
  353.         ShowErrAndQuit("SndNewChannel", err);
  354.     
  355.     // finally play our sound file
  356.     
  357.     err = SndStartFilePlay(mySoundChannel, myFRefNum, 0, 40000L, 0, 0, 0, false);
  358.     if (err != noErr)
  359.         ShowErrAndQuit("SndStartFilePlay", err);
  360.  
  361.     // dispose of the sound channel
  362.     
  363.     SndDisposeChannel(mySoundChannel, true);
  364.  
  365.     // close the file
  366.     
  367.     FSClose(myFRefNum);
  368.     
  369.     // cleanup
  370.     
  371.     if (sampleSizesInfo.sizesSupported)
  372.         DisposeHandle((Handle)sampleSizesInfo.sizesSupported);
  373.     if (sampleRatesInfo.ratesSupported)
  374.         DisposeHandle((Handle)sampleRatesInfo.ratesSupported);
  375.     }
  376.  
  377. void ShowErr(char*text, short err)
  378.     {
  379.     char s[256];
  380.     if (err)
  381.         sprintf((char*)s, "Failure at %s, with error = %hd.", text, err);
  382.     else
  383.         sprintf((char*)s, "%s.", text);
  384.     c2pstr(s);
  385.     ParamText((Byte*)s, 0, 0, 0);
  386.     NoteAlert(128, 0);
  387.     }
  388.  
  389. void ShowErrAndQuit(char*text, short err)
  390.     {
  391.     ShowErr( text, err );
  392.     ExitToShell();
  393.     }
  394.